package com.fy.proj5_permission_transaction_cache.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fy.proj5_permission_transaction_cache.service.CacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class CacheServiceImpl implements CacheService {

    private static final Logger logger = LoggerFactory.getLogger(CacheServiceImpl.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    // 默认过期时间配置
    private static final long DEFAULT_EXPIRATION = 1;
    private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.HOURS;
    
    // 批量操作的批次大小，防止一次性操作过多键导致Redis阻塞
    private static final int BATCH_SIZE = 1000;

    @Override
    public boolean set(String key, Object value) {
        if (key == null || value == null) {
            logger.warn("Cache set failed: key or value is null");
            return false;
        }
        
        try {
            // 添加键前缀以区分业务缓存
            String cacheKey = getCacheKey(key);
            redisTemplate.opsForValue().set(cacheKey, value, DEFAULT_EXPIRATION, DEFAULT_TIME_UNIT);
            logger.debug("Cache set success: {}", cacheKey);
            return true;
        } catch (Exception e) {
            logger.error("Cache set failed: {}, error: {}", key, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean set(String key, Object value, long timeout) {
        if (key == null || value == null) {
            logger.warn("Cache set failed: key or value is null");
            return false;
        }
        
        // 防止设置过长的过期时间
        if (timeout > 365L * 24 * 60 * 60) {
            timeout = 365L * 24 * 60 * 60;
            logger.warn("Cache timeout too long, limited to 365 days: {}", key);
        }
        
        try {
            String cacheKey = getCacheKey(key);
            redisTemplate.opsForValue().set(cacheKey, value, timeout, TimeUnit.SECONDS);
            logger.debug("Cache set success with timeout: {}, {} seconds", cacheKey, timeout);
            return true;
        } catch (Exception e) {
            logger.error("Cache set failed: {}, error: {}", key, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean set(String key, Object value, long timeout, TimeUnit unit) {
        if (key == null || value == null) {
            logger.warn("Cache set failed: key or value is null");
            return false;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            redisTemplate.opsForValue().set(cacheKey, value, timeout, unit);
            logger.debug("Cache set success with timeout: {}, {} {}", cacheKey, timeout, unit);
            return true;
        } catch (Exception e) {
            logger.error("Cache set failed: {}, error: {}", key, e.getMessage());
            return false;
        }
    }

    @Override
    public Object get(String key) {
        if (key == null) {
            return null;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            return redisTemplate.opsForValue().get(cacheKey);
        } catch (Exception e) {
            logger.error("Cache get failed: {}, error: {}", key, e.getMessage());
            return null;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> type) {
        if (key == null || type == null) {
            return null;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            Object value = redisTemplate.opsForValue().get(cacheKey);
            
            if (value == null) {
                return null;
            }
            
            // 如果是同一类型，直接返回
            if (type.isInstance(value)) {
                return (T) value;
            }
            
            // 如果是字符串类型，尝试反序列化
            if (value instanceof String && !String.class.equals(type)) {
                try {
                    return objectMapper.readValue((String) value, type);
                } catch (Exception e) {
                    logger.error("Failed to deserialize cache value for key: {}", key, e);
                    // 反序列化失败时删除损坏的缓存
                    delete(key);
                    return null;
                }
            }
            
            return null;
        } catch (Exception e) {
            logger.error("Cache get failed: {}, error: {}", key, e.getMessage());
            return null;
        }
    }

    @Override
    public boolean delete(String key) {
        if (key == null) {
            return false;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            boolean result = Boolean.TRUE.equals(redisTemplate.delete(cacheKey));
            if (result) {
                logger.debug("Cache deleted: {}", cacheKey);
            }
            return result;
        } catch (Exception e) {
            logger.error("Cache delete failed: {}, error: {}", key, e.getMessage());
            return false;
        }
    }

    @Override
    public void deleteBatch(String[] keys) {
        if (keys == null || keys.length == 0) {
            return;
        }
        
        try {
            // 转换为带前缀的键
            List<String> cacheKeys = new ArrayList<>();
            for (String key : keys) {
                if (key != null) {
                    cacheKeys.add(getCacheKey(key));
                }
            }
            
            // 分批删除，防止Redis阻塞
            for (int i = 0; i < cacheKeys.size(); i += BATCH_SIZE) {
                int end = Math.min(i + BATCH_SIZE, cacheKeys.size());
                redisTemplate.delete(cacheKeys.subList(i, end));
            }
            
            logger.debug("Cache batch deleted, count: {}", cacheKeys.size());
        } catch (Exception e) {
            logger.error("Cache batch delete failed, error: {}", e.getMessage());
        }
    }

    @Override
    public int clearByPrefix(String prefix) {
        if (prefix == null || prefix.isEmpty()) {
            logger.warn("Clear by prefix failed: prefix is null or empty");
            return 0;
        }
        
        // 添加安全检查，防止清除所有缓存
        if (prefix.equals("*") || prefix.equals("")) {
            logger.error("Dangerous operation prevented: trying to clear cache with unsafe prefix: {}", prefix);
            return 0;
        }
        
        try {
            String cachePrefix = getCacheKey(prefix);
            List<String> keys = new ArrayList<>(redisTemplate.keys(cachePrefix + "*"));
            
            if (!keys.isEmpty()) {
                // 分批删除
                int totalDeleted = 0;
                for (int i = 0; i < keys.size(); i += BATCH_SIZE) {
                    int end = Math.min(i + BATCH_SIZE, keys.size());
                    List<String> batchKeys = keys.subList(i, end);
                    redisTemplate.delete(batchKeys);
                    totalDeleted += batchKeys.size();
                }
                
                logger.info("Cache cleared by prefix: {}, deleted count: {}", cachePrefix, totalDeleted);
                return totalDeleted;
            }
            return 0;
        } catch (Exception e) {
            logger.error("Clear by prefix failed: {}, error: {}", prefix, e.getMessage());
            return 0;
        }
    }

    @Override
    public boolean exists(String key) {
        if (key == null) {
            return false;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            return redisTemplate.hasKey(cacheKey);
        } catch (Exception e) {
            logger.error("Cache exists check failed: {}, error: {}", key, e.getMessage());
            return false;
        }
    }

    @Override
    public long increment(String key) {
        if (key == null) {
            return 0;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            return redisTemplate.opsForValue().increment(cacheKey);
        } catch (Exception e) {
            logger.error("Cache increment failed: {}, error: {}", key, e.getMessage());
            return 0;
        }
    }

    @Override
    public long decrement(String key) {
        if (key == null) {
            return 0;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            return redisTemplate.opsForValue().decrement(cacheKey);
        } catch (Exception e) {
            logger.error("Cache decrement failed: {}, error: {}", key, e.getMessage());
            return 0;
        }
    }

    @Override
    public boolean expire(String key, Long expireTime) {
        if (key == null || expireTime == null || expireTime < 0) {
            return false;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            return redisTemplate.expire(cacheKey, expireTime, TimeUnit.SECONDS);
        } catch (Exception e) {
            logger.error("Cache expire set failed: {}, error: {}", key, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean expire(String key, long timeout, TimeUnit unit) {
        if (key == null || unit == null) {
            return false;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            return redisTemplate.expire(cacheKey, timeout, unit);
        } catch (Exception e) {
            logger.error("Cache expire set failed: {}, error: {}", key, e.getMessage());
            return false;
        }
    }

    @Override
    public Long getExpire(String key) {
        if (key == null) {
            return null;
        }
        
        try {
            String cacheKey = getCacheKey(key);
            return redisTemplate.getExpire(cacheKey);
        } catch (Exception e) {
            logger.error("Get cache expire failed: {}, error: {}", key, e.getMessage());
            return null;
        }
    }

    @Override
    public void clearAll() {
        // 添加安全机制，生产环境禁止直接清除所有缓存
        logger.warn("Clear all cache operation called, use with caution!");
        try {
            // 使用RedisCallback进行原子操作
            redisTemplate.execute((RedisCallback<Void>) connection -> {
                connection.flushAll();
                return null;
            });
            logger.info("All cache cleared");
        } catch (Exception e) {
            logger.error("Clear all cache failed, error: {}", e.getMessage());
        }
    }

    @Override
    public void clear() {
        clearAll();
    }
    
    @Override
    public List<String> getKeysByPrefix(String prefix) {
        if (prefix == null || prefix.isEmpty()) {
            logger.warn("Get keys by prefix failed: prefix is null or empty");
            return new ArrayList<>();
        }
        
        // 添加安全检查，防止获取所有键
        if (prefix.equals("*") || prefix.equals("")) {
            logger.error("Dangerous operation prevented: trying to get keys with unsafe prefix: {}", prefix);
            return new ArrayList<>();
        }
        
        try {
            // 添加业务前缀
            String cachePrefix = getCacheKey(prefix);
            Set<String> keys = redisTemplate.keys(cachePrefix + "*");
            
            if (keys != null && !keys.isEmpty()) {
                // 移除业务前缀，返回原始键
                List<String> result = new ArrayList<>();
                for (String key : keys) {
                    // 检查并移除user:前缀
                    if (key.startsWith("user:")) {
                        result.add(key.substring("user:".length()));
                    } 
                    // 检查并移除page:前缀
                    else if (key.startsWith("page:")) {
                        result.add(key);
                    }
                    // 其他情况直接返回
                    else {
                        result.add(key);
                    }
                }
                logger.debug("Got keys by prefix: {}, count: {}", prefix, result.size());
                return result;
            }
            return new ArrayList<>();
        } catch (Exception e) {
            logger.error("Get keys by prefix failed: {}, error: {}", prefix, e.getMessage());
            return new ArrayList<>();
        }
    }
    
    /**
     * 为键添加业务前缀，用于区分不同业务的缓存
     */
    private String getCacheKey(String key) {
        // 已经包含前缀的键不再添加
        if (key.startsWith("user:") || key.startsWith("page:")) {
            return key;
        }
        return "user:" + key;
    }
}